﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FarseerGames.FarseerPhysics.Collisions;
using LaughingDog.GameObjects;
using FarseerGames.FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using LaughingDog.Managers;
using LaughingDog.Input;
using LaughingDog.Components.Shapes;

namespace LaughingDog.Components
{
    public class Collider : Component
    {
        protected static Dictionary<Geom, Collider> mGeomColliderPairs = new Dictionary<Geom, Collider>();

        public delegate Boolean CollisionEventHandler(Collider collider1, Collider collider2, ContactList contactList);
        public delegate void SeperationEventHandler(Collider collider1, Collider collider2);

        protected Geom mGeom = null;        
        private Boolean mCollided = false;
        private Vector2 mScale;

        private ulong mTicks = 0;
        private ulong mTicksOnCollision = 0;
        private bool mHasCollision = false;

        /// <summary>
        /// The underlying Farseer Geom object used for this collider.
        /// </summary>
        public Geom Geom { get { return mGeom; } set { mGeom = value; } }

        /// <summary>
        /// The RigidBody that is attached to this Collider.
        /// </summary>
        public RigidBody AttachedRigidBody { get { return GameObject.RigidBody; } }

        /// <summary>
        /// Is this Collider a Trigger? Triggers have no collision response causing them to act like ghosts.
        /// </summary>
//        public Boolean IsTrigger { get { return !mGeom.CollisionResponseEnabled; } set { mGeom.CollisionResponseEnabled = !value; } }
        public Boolean IsTrigger { get { return mGeom.IsSensor; } set { mGeom.IsSensor = value; } }

        /// <summary>
        /// The collision group this Collider is in.
        /// </summary>
        public Int32 CollisionGroup { get { return mGeom.CollisionGroup; } set { mGeom.CollisionGroup = value; } }

        /// <summary>
        /// Gets or sets the restitution of this Collider.
        /// </summary>
        public Single Restitution { get { return mGeom.RestitutionCoefficient; } set { mGeom.RestitutionCoefficient = value; } }

        /// <summary>
        /// Gets or sets the friction of this Collider.
        /// </summary>
        public Single Friction { get { return mGeom.FrictionCoefficient; } set { mGeom.FrictionCoefficient = value; } }

        /// <summary>
        /// Gets a value indicating whether this Collider is currently colliding or not.
        /// </summary>
        public Boolean IsColliding { get { return mHasCollision; } }

        /// <summary>
        /// Fired once when a non-trigger Collider collides with another Collider.
        /// </summary>
        public CollisionEventHandler OnCollisionEnter;

        /// <summary>
        /// Fired every frame when a non-trigger Collider is colliding with another Collider.
        /// </summary>
        public CollisionEventHandler OnCollisionStay;

        /// <summary>
        /// Fired once when a non-trigger Collider stops colliding with another Collider.
        /// </summary>
        public SeperationEventHandler OnCollisionExit;

        /// <summary>
        /// Fired once when a trigger Collider collides with another Collider.
        /// </summary>
        public CollisionEventHandler OnTriggerEnter;

        /// <summary>
        /// Fired every frame when a trigger Collider is colliding with another Collider.
        /// </summary>
        public CollisionEventHandler OnTriggerStay;

        /// <summary>
        /// Fired once when a trigger Collider collides with another Collider.
        /// </summary>
        public SeperationEventHandler OnTriggerExit;

        /// <summary>
        /// Creates an instance of a Collider component.
        /// </summary>
        /// <param name="gameObject">The GameObject that this component should be attached to.</param>
        public Collider(GameObject gameObject)
            : base(gameObject)
        {
            mScale = Transform.Scale;            
        }   

        public override void Update()
        {
            ++mTicks;

            if (mScale != Transform.Scale)
            {
                Vector2 tmpScale = GameObject.Transform.Scale;
                mGeom.LocalVertices.Scale(ref tmpScale);

                mScale = Transform.Scale;
            }

            base.Update();
        }

        protected Boolean Collision(Geom geom1, Geom geom2, ContactList contactList)
        {
            mTicksOnCollision = mTicks;
            mHasCollision = true;

            Collider collider1 = GetColliderFromGeom(geom1);
            Collider collider2 = GetColliderFromGeom(geom2);

            if (IsTrigger)
            {
                if (!mCollided)
                {
                    mCollided = true;

                    if (OnTriggerEnter != null)
                    {
                        return OnTriggerEnter(collider1, collider2, contactList);
                    }
                }
                else
                {
                    if (OnTriggerStay != null)
                        return OnTriggerStay(collider1, collider2, contactList);
                }
            }
            else
            {
                if (!mCollided)
                {
                    mCollided = true;

                    if (OnCollisionEnter != null)
                    {
                        return OnCollisionEnter(collider1, collider2, contactList);
                    }
                }
                else
                {
                    if (OnCollisionStay != null)
                        return OnCollisionStay(collider1, collider2, contactList);
                }
            }            
            
            return true;
        }

        protected void Seperation(Geom geom1, Geom geom2)
        {            
            Collider collider1 = GetColliderFromGeom(geom1);
            Collider collider2 = GetColliderFromGeom(geom2);

            if (mTicksOnCollision < mTicks)
            {
                if (IsTrigger)
                {
                    if (OnTriggerExit != null)
                        OnTriggerExit(collider1, collider2);
                }
                else
                {
                    if (OnCollisionExit != null)
                        OnCollisionExit(collider1, collider2);
                }

                mCollided = false;
                mHasCollision = false;
            }                   
        }

        //public static void AddGeomToColliderDictionary(Geom geom, Collider collider)
        //{
        //    mGeomColliderPairs.Add(geom, collider);
        //}

        public static Collider GetColliderFromGeom(Geom geom)
        {
            if(mGeomColliderPairs.ContainsKey(geom))
                return mGeomColliderPairs[geom];

            return null;
        }

        public PolygonShape CreateColliderShape()
        {
            // These need to be changed off the offset.
            PolygonShape shape = GameObject.AddComponent<PolygonShape>();
            shape.SetVectors(mGeom.LocalVertices);
                        
            shape.Join();            

            return shape;
        }
    }
}
